﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using ClearUnit.TestRunners.Starts;
using ClearUnit.TestRunners.Results;
using Common.Net.DesignByContract;
using Common.Net.Utilities;

namespace ClearUnit.TestRunners
{
    public class TypeRunner : TestRunnerBase<TypeRunResult>
    {
        readonly Type _type;
        readonly bool _explicitRun;

        public TypeRunner(Type type, bool explicitRun, params Action<TestRunInfo>[] interestedListeners)
            : base(interestedListeners)
        {
            RequireArg.NotNull(() => type);

            _type = type;
            _explicitRun = explicitRun;
        }

        protected sealed override TypeRunResult RunInternal()
        {
            TypeRunResult runResult;

            if (!_explicitRun && _type.IsExplicit())
                runResult = new TypeRunResult.Skipped(_type, GetMethodsToRun().Select(m => (MethodRunResult)new MethodRunResult.Skipped(m)).ToList());
            else
            {
                OnTestInfoEvent(new TypeRunStart(_type));

                var methodResults = new List<MethodRunResult>();

                foreach (var method in GetMethodsToRun())
                {
                    var methodRunner = new MethodRunner(method, false, OnTestInfoEvent);
                    var testResultCollector = new TestResultCollector(methodRunner);

                    methodRunner.Run();

                    methodResults.AddRange(testResultCollector.MethodResults);
                }

                runResult = new TypeRunResult.Run(_type, methodResults);
            }

            OnTestInfoEvent(runResult);

            return runResult;
        }

        private IEnumerable<MethodInfo> GetMethodsToRun()
        {
            foreach (var method in TypeAttributeHelper.GetTestMethods(_type))
                if (ShouldRunTypeMethod(method))
                    yield return method;
        }

        static bool ShouldRunTypeMethod(MethodInfo method)
        {
            if (method.DeclaringType == typeof(object))
                return false;

            if (!method.IsTest())
                return false;

            return true;
        }
    }
}
